#encoding:UTF-8

from py_at.rpc.RecObject import RpcObject
import zmq
import threading
import traceback
import time
class RpcServer(RpcObject):
    """RPC服务器"""
    #----------------------------------------------------------
    def __init__(self,repAddress,pubAddress):
        """Constructor"""
        super(RpcServer, self).__init__()
        # 保存功能函数的字典，key是函数名，value是函数对象
        self.__functions = {}
        #ZMQ端口相关
        self.__context =zmq.Context()
        self.__socketREP = self.__context.socket(zmq.REP) #请求回应socket
        self.__sockerPUB = self.__context.socket(zmq.PUB)  #广播socket
        self.__socketREP.bind(repAddress)
        self.__sockerPUB.bind(pubAddress)
        self.__active = False
        self.__thread = threading.Thread(target=self.run)
    #--------------------------------------------------------------
    def start(self):
        """启动服务器"""
        self.__active = True

        #启动工作线程
        if not self.__thread.isAlive():
            self.__thread.start()

    #-----------------------------------------------------------------
    def stop(self):
        #停止服服务
        self.__active =False

        #等待工作线程退出
        if self.__thread.isAlive():
            self.__thread.join()
    #-----------------------------------------------------------------
    def run(self):
        """服务器运行函数"""
        while self.__active:

            # 使用poll来等待时间到达，等待1
            if not self.__socketREP.poll(1000):
                continue
            # 从请求相应socket 收取请求数据
            reqb = self.__socketREP.recv()
            #序列化解包
            req = self.unpack(reqb)
            #获取函数名和参数
            name,args,kwargs = req
            #获取引擎中对应的函数对象，并执行调用，如果有异常则捕捉后返回
            try:
                #print "接收到前台的调用 name %s args %s " %(name,args)
                func = self.__functions[name]        #里面是外层注入的 call 函数   所以这里等于调用了call函数
                r = func(*args,**kwargs)                  #调用call 函数
                rep = [True,r]
            except Exception as e:
                rep = [False,traceback.format_exc()]     #traceback.输出异常的行数以及块位置

            #序列化打包
            repb = self.pack(rep)
            #通过请求相应socket返回结果
            self.__socketREP.send(repb)

    #-------------------------------------------------
    def publish(self,topic,data):
        """
        广播推送数据
        :param topic:主题内容(注意必须是ascii编码)
        :param data: 具体的数据
        :return:
        """
        datab = self.pack(data)
        topic= self.pack(topic)
        #通过官博socket发送数据
        self.__sockerPUB.send_multipart([topic,datab])


    #------------------------------------------------------
    def register(self,func):
        """注册函数"""
        self.__functions[func.__name__]=func




class ReEngine(object):
    """RPC服务引擎"""
    name = u'RPC服务'
    #-------------------------------------------------------------
    def __init__(self,mainEnging,eventEngine):
        self.mainEngine = mainEnging
        self.eventEgine = eventEngine
        self.sever = None  # RPC服务对象
        self.reqAddress = "" #REP地址
        self.pubAddress = "" #PUB地址
        self.functionDict = {} #调用过得函数对象缓存字典
        self.init()
        self.registerEvent()
    #----------------------------------------------------------------
    def init(self):
        self.reqAddress ="tcp://*:2014"
        self.pubAddress ="tcp://*:0602"
        self.server = RpcServer(self.reqAddress,self.pubAddress)
        self.server.usePickle()
        self.server.register(self.call)         #把当前函数call注册到server里面（server的REQ接口接受到前台的调用数据之后使用这个函数进行查找主对象的函数并且执行）  然后返回
        self.server.start()
    def registerEvent(self):
        self.eventEgine.register(self.processEvent)        #这个位置把ctp返回信息之后调用的函数注入进去  等于ctp如果有信息回来直接调用这个函数
    def call(self,d):
        """调用服务端主引擎的函数 然后进行返回结果"""
        def iscallable(func,*args,**kwargs):
            """判断是否是可调用对象 并返回合适的调用"""
            if callable(func):
                return func(*args,**kwargs)
            else:
                return func
        nameList = d['nameList'] #对象属性列表
        nameTuple = tuple(nameList) #转化为元祖
        args = d['args']
        kwargs = d['kwargs']
        print("RPC后台接到前台来了调用 %s" %d)
        #如果已经有缓存了 ，直接返回
        if nameTuple in self.functionDict:
            function = self.functionDict[nameTuple]
            return iscallable(function,*args,**kwargs)
        #逐层寻找函数对象
        else:
            #根对象为主引擎
            obj = self.mainEngine
            # 逐层寻找对象属性
            print ("self.mainEngine 是%s  nameTuple = %s" %(self.mainEngine,nameTuple))
            for name in nameTuple:
                obj = obj.__getattribute__(name)
            # 缓存结果
            self.functionDict[nameTuple] = obj
            #调用最终的对象
            return iscallable(obj,*args,**kwargs)
    #-----------------------------------------------------------------
    def processEvent(self,event):
        """处理时间推送
            订阅了所有对象
        """
        self.server.publish('',event)
    #------------------------------------------------------------------
    def stop(self):
        self.server.stop()








#------------------------------------------------------------------------------------------------------------------------------------------简单测试定义的服务器类
class DictS(object):

    """服务端模拟对象"""
    def __init__(self):
        self._thred = threading.Thread(target=self.run)
        self.canshu= "DictS"
    #-----------------------------------------------------------------------------
    def dome1(self):
        """测试了dome1"""
        return "调用了dome1"
    #-----------------------------------------------------------------------------
    def dome2(self,s):
        """测试了dome2"""
        return "调用了dome2 参数是  %s" %s
    #______________________________________________________________________________
    def register(self,func):
        self.func = func           #谁想使用这个数据就从外部注入进来   使用方法register(自定义函数)
    #------------------------------------------------------------------------------
    def run(self):
        """
        模仿相应事件  例 ctp数据返回信息 这个地方的函数是被动注册的
        :return:
        """
        while True:
            time.sleep(1)
            strTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            self.func(strTime)

def jiandaceshi():
    """简单测试"""
    mainEnging = DictS()
    eventEngine = DictS()

    reE = ReEngine(mainEnging, eventEngine)
    eventEngine._thred.start()
#-----------------------------------------------------------------------------------------------------------------------配套简单测试类做的服务器测试函数

from py_at.py_at_adapter_test import *
def shijiceshi():
    eventEngine = mainEngine = AdapterTest()
    reE = ReEngine(mainEngine, eventEngine)
if __name__ == "__main__":
    #jiandaceshi()
    shijiceshi()





